/* 
 * jVSTwRapper - The Java way into VST world!
 * 
 * jVSTwRapper is an easy and reliable Java Wrapper for the Steinberg VST interface. 
 * It enables you to develop VST 2.3 compatible audio plugins and virtual instruments 
 * plus user interfaces with the Java Programming Language. 3 Demo Plugins(+src) are included!
 * 
 * Copyright (C) 2006  Daniel Martin [daniel309@users.sourceforge.net] 
 * 					   and many others, see CREDITS.txt
 *
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 */

package vapu.examples.jaydelay;

import jass.engine.BufferNotAvailableException;

import org.jaudiolibs.audioservers.AudioConfiguration;
import org.tritonus.share.sampled.FloatSampleBuffer;

import themidibus.listener.SimpleMidiListener;
import im.composer.audio.engine.Source;
import im.composer.vapu.VAPU;

public class JayDLay extends VAPU implements SimpleMidiListener{
	public final static int PARAM_ID_DELAY = 0;
	public final static int PARAM_ID_FEEDBACK = 1;
	public final static int PARAM_ID_OUT = 2;

	public final static int NUM_PARAMS = PARAM_ID_OUT + 1;

	private DelayProgram[] programs;
	private float[][] buffer;
	private float fDelay, fFeedBack, fOut;
	private int delay;
	private int cursor;
	private int currentProgram;

	public JayDLay() {

		this.cursor = 0;
		this.delay = 0;

		this.programs = new DelayProgram[16];
		for (int i = 0; i < this.programs.length; i++) {
			this.programs[i] = new DelayProgram();
		}
		this.fDelay = this.fFeedBack = 0F;
		this.fOut = 1.0F;
	}

	@Override
	public synchronized void configure(AudioConfiguration context) throws Exception {
		super.configure(context);
		int nChannels = Math.min(getContext().getInputChannelCount(), getContext().getOutputChannelCount());
		this.buffer = new float[nChannels][(int) context.getSampleRate()];
	}

	public int getProgram() {
		return this.currentProgram;
	}

	public float getParameter(int index) {
		float v = 0;

		switch (index) {
		case PARAM_ID_DELAY:
			v = this.fDelay;
			break;
		case PARAM_ID_FEEDBACK:
			v = this.fFeedBack;
			break;
		case PARAM_ID_OUT:
			v = this.fOut;
			break;
		}

		return v;
	}

	public String getParameterName(int index) {
		String label = "";

		switch (index) {
		case PARAM_ID_DELAY:
			label = "Delay";
			break;
		case PARAM_ID_FEEDBACK:
			label = "FeedBack";
			break;
		case PARAM_ID_OUT:
			label = "Volume";
			break;
		}

		return label;
	}

	public String getProductString() {
		return "JayDLay 1.0";
	}

	public void setProgram(int index) {
		DelayProgram dp = this.programs[index];

		// log("setProgram index=" + index);

		this.currentProgram = index;

		this.setParameter(PARAM_ID_DELAY, dp.getDelay());
		this.setParameter(PARAM_ID_FEEDBACK, dp.getFeedback());
		this.setParameter(PARAM_ID_OUT, dp.getOut());

	}

	public void setParameter(int index, float value) {
		DelayProgram dp = this.programs[this.currentProgram];

		// log("setParameter index=" + index + " value=" + value);

		switch (index) {
		case PARAM_ID_DELAY:
			this.setDelay(value);
			break;
		case PARAM_ID_FEEDBACK:
			this.fFeedBack = value;
			dp.setFeedback(value);
			break;
		case PARAM_ID_OUT:
			this.fOut = value;
			dp.setOut(value);
			break;
		}

	}

	public String getEffectName() {
		return "JayDLay";
	}

	public String getParameterLabel(int index) {
		String label = "";

		switch (index) {
		case PARAM_ID_DELAY:
			label = "";
			break;
		case PARAM_ID_FEEDBACK:
			label = "";
			break;
		case PARAM_ID_OUT:
			label = "dB";
			break;
		}

		return label;
	}

	public String getProgramName() {
		String name;

		if (this.programs[this.currentProgram].getName().equals("Init")) {
			name = this.programs[this.currentProgram].getName() + " " + (this.currentProgram + 1);
		} else {
			name = this.programs[this.currentProgram].getName();
		}

		return name;
	}

	public String getProgramNameIndexed(int category, int index) {
		String text = "";
		if (index < this.programs.length)
			text = this.programs[index].getName();
		if ("Init".equals(text))
			text = text + " " + index;

		return text;
	}

	public void processBuffer(int channel,float[]in, float[] out, int sampleFrames) {

		for (int i = 0; i < sampleFrames; i++) {
			float x = in[i];
			float y = this.buffer[channel][this.cursor] * this.fOut;

			this.buffer[channel][this.cursor++] = x + y * this.fFeedBack;
			if (this.cursor >= this.delay)
				this.cursor = 0;

			out[i] = y;
		}
	}

	private void setDelay(float fdelay) {
		this.fDelay = fdelay;
		this.programs[this.currentProgram].setDelay(fdelay);
		// this.cursor = 0;
		this.delay = (int) (fdelay * (getContext().getSampleRate() - 1));
	}

	@Override
	public void activeSensing(long timeStamp) {
		
	}

	@Override
	public void noteOn(int channel, int pitch, int velocity) {
		
	}

	@Override
	public void noteOff(int channel, int pitch, int velocity) {
		
	}

	@Override
	public void controllerChange(int channel, int number, int value) {
		
	}

	@Override
	public void programChange(int channel, int value) {
		
	}

	@Override
	public void pitchBend(int channel, float val) {
		
	}

	@Override
	public void channelPressure(int channel, float val) {
		
	}

	@Override
	public void channelVolume(int channel, float val) {
		
	}

	@Override
	public void sustainChange(boolean b) {
		
	}

	@Override
	protected void executeCommand(long timeStamp, String command) {
		
	}

	@Override
	protected void computeBuffer() {
		int nChannels = Math.min(getContext().getInputChannelCount(), getContext().getOutputChannelCount());
		FloatSampleBuffer input = new FloatSampleBuffer(nChannels, getContext().getMaxBufferSize(), getContext().getSampleRate());
		for (Source src : getSources()) {
			try {
				input.mix(src.getBuffer());
			} catch (BufferNotAvailableException e) {
			}
		}
		for (int i = 0; i < nChannels; i++) {
			float[] frames = input.getChannel(i);
			processBuffer(i, frames, frames, getContext().getMaxBufferSize());
			buf.setRawChannel(i, frames);
		}		
	}

}

/**
 * Helper Class (VO) for the support of different presets for the plug if you
 * are using no gui (comment PluginUIClass out in jVSTwRapper.ini)
 * 
 * @author dm
 * @version 1.0
 */
class DelayProgram {
	private String name = "Init";
	private float delay = 0.5F;
	private float feedback = 0.5F;
	private float out = 1F;

	public String getName() {
		return this.name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public float getDelay() {
		return this.delay;
	}

	public void setDelay(float delay) {
		this.delay = delay;
	}

	public float getFeedback() {
		return this.feedback;
	}

	public void setFeedback(float feedback) {
		this.feedback = feedback;
	}

	public float getOut() {
		return this.out;
	}

	public void setOut(float out) {
		this.out = out;
	}
}